package types

import (
	"fmt"
	"gitee.com/sunliang711/locker-control/server/utils"
	"github.com/gorilla/mux"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	errorLimit = 10
	//read write timeout
	readWriteTimeout      = 5
	checkLivenessInterval = 60
	livenessTimeout       = 60
)

type Version byte

const (
	Version2G Version = iota + 2
	Version3G
	Version4G
)

func (v Version) String() string {
	switch v {
	case Version2G:
		return "2G"
	case Version4G:
		return "4G"
	default:
		return "Unknown protocol version!"
	}
}

var (
	Peers = make(map[string]*Peer)
	// 2g or 4g
	ProtocolVersion Version
)

type Server struct {
	//for locker client
	Protocol              string
	Address               string
	CheckLivenessInterval int
	LivenessTimeout       int

	//restful API router
	Router     *mux.Router
	APIAddress string

	quit chan struct{}
}

func NewServer(lkSrvAddress string, apiAddress string, ver Version, interval int, livenessTimeout int) (*Server, error) {
	switch ver {
	case Version2G, Version4G:
	default:
		return nil, fmt.Errorf("Protocol version invalid")
	}
	srv := &Server{

		Protocol:              "tcp",
		Address:               lkSrvAddress,
		CheckLivenessInterval: interval,
		LivenessTimeout:       livenessTimeout,

		Router:     mux.NewRouter(),
		APIAddress: apiAddress,

		quit: make(chan struct{}),
	}
	srv.RegisterHandler()

	ProtocolVersion = ver

	return srv, nil
}

func (srv *Server) RegisterHandler() {
	srv.Router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("locker control api root"))
	})
	srv.Router.HandleFunc("/open/{id}/{channel}", LockerOpen)
	srv.Router.HandleFunc("/status/{id}/{channel}", LockerStatus)
	srv.Router.HandleFunc("/peers", GetAllPeers)
}

func (srv *Server) Start() {
	log.Printf("Protocol Version: %s", ProtocolVersion)

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		sig := <-sigs
		log.Printf("Got signal: %v", sig)
		srv.Stop()
	}()

	go srv.checkLiveness()
	//API server
	go srv.startRestAPI()

	go srv.startLockerServer()

	select {
	case <-srv.quit:
		log.Printf("Quit.")
	}
}

func handleConn(conn net.Conn) {

	buf := make([]byte, 255)
	var (
		id  string
		err error
	)
	switch ProtocolVersion {
	case Version2G:
		id, err = deviceId2g(conn)
		if err != nil {
			log.Printf("  Get divice Id error: %s", err)
		}
	case Version4G:
		id, err = deviceId4g(conn)
		if err != nil {
			log.Printf("  Get device Id error: %s", err)
		}
	}
	log.Printf("  Got device Id: %s", id)
	Peers[id] = &Peer{
		Id:         id,
		Conn:       conn,
		ResponseCh: make(chan []byte),
		PingTime:   time.Now(),
	}
	log.Printf("Peers: %v", Peers)
	//make Peer and push to Peers
	for {
		//这里从客户端读取消息,包括3种:心跳包(需要服务端回复);客户端主动上传门状态变化;客户端对服务端命令的响应;
		log.Printf("  Waiting for message from client: %s ...", id)
		n, err := conn.Read(buf)
		if err != nil {
			log.Printf("  Read packet error from client: %s, error message: %s", id, err)
			break
		}
		//从buf[:n]中解析出板号,然后从Peers中找到对应的Peer,然后根据消息类型进行对应的处理
		//比如回复心跳包,以及客户端主动上传门状态变化,以及客户端对服务端命令的响应
		log.Printf("  Client [id: %v] > :%v,Raw data: %v", id, buf[:n], utils.HexBytes(buf[:n]))
		//如果是客户端回复的消息,则发送到对应的channel里面
		frame, err := NewFrameFromBytes(buf[:n])
		if err != nil {
			log.Printf("  Error: Client (id: %v) > %s", err.Error())
			break
		}
		handleCmd(frame, id)
	}
}

func deviceId2g(conn net.Conn) (string, error) {
	buf := make([]byte, 255)
	log.Printf("  Waiting 2g register packet...")
	n, err := conn.Read(buf)
	if err != nil {
		return "", fmt.Errorf("  deviceId2g: read client error: %v", err)
	}
	fr, err := NewFrameFromBytes(buf[:n])
	if err != nil {
		return "", fmt.Errorf("  Register packet format error: %s", err)
	}

	return string(fr.Data), nil

}

func deviceId4g(conn net.Conn) (string, error) {
	buf := make([]byte, 255)
	var (
		n        int
		err      error
		errCount int
	)
	//从客户端读取注册包或者心跳包,然后向客户端查询设备Id
	//从客户端读取心跳包,如果不是连接上就发送心跳包的话,为了保险起见
	//延迟30秒后,根据文档,客户端会自动发送心跳包,这时候从心跳包读取板号
	//read from conn,get device Id
	for {
		log.Printf("Try to read register packet...")
		n, err = conn.Read(buf)
		if err != nil {
			errCount += 1
			log.Printf("Read first packet error: %d,msg: %v\n ", errCount, err)
			if errCount >= errorLimit {
				return "", fmt.Errorf("Read register packet count greater than %v", errorLimit)
			}
			continue
		}
		log.Printf("Got register packet: %v {%v}\n", buf[:n], utils.HexBytes(buf[:n]))
		break
	}

	//TODO 多次查询
	//向客户端查询设备Id
	frame := NewFrame(CmdId4G, nil)
	n, err = conn.Write(frame.Bytes())
	if err != nil || n != len(frame.Bytes()) {
		return "", fmt.Errorf("Send query device id packet error: %v", err)
	}
	log.Printf("Sent query device id packet OK")

	log.Printf("Try to got device id packet from client...")
	n, err = conn.Read(buf)
	if err != nil {
		return "", fmt.Errorf("Read device id response error: %v", err)
	}
	newFrame, err := NewFrameFromBytes(buf[:n])
	if err != nil {
		return "", fmt.Errorf("Id message format error")
	}
	//TODO check 'cmd' field
	id := string(newFrame.Data)
	log.Printf("Got device id: %v", id)

	return id, nil
}

func handleCmd(frame *Frame, id string) {
	switch frame.Cmd {
	case CmdHeart2G:
		Peers[id].PingTime = time.Now()
		log.Printf("    Client [id: %s] >> PING", id)
		pong := NewFrame(CmdHeart2G, nil)
		n, err := Peers[id].WriteTimeout(pong.Bytes())
		if err != nil || n != len(pong.Bytes()) {
			log.Printf("    Client [id: %s] !<< PONG, error: %v", id, err)
			return
		}
		log.Printf("    Client [id: %s] << PONG", id)
	case CmdHeart4G:
		Peers[id].PingTime = time.Now()
		log.Printf("    Client [id: %s] >> PING", id)
		pong := NewFrame(CmdHeart4G, nil)
		Peers[id].Conn.SetWriteDeadline(time.Now().Add(time.Second * readWriteTimeout))
		n, err := Peers[id].WriteTimeout(pong.Bytes())
		if err != nil || n != len(pong.Bytes()) {
			log.Printf("    Client [id: %s] !<< PONG, error: %v", id, err)
			return
		}
		log.Printf("    Client [id: %s] << PONG", id)
	case CmdClientPushStatus2G:
		log.Printf("    TODO: CmdClientPush2G Status")
	case CmdClientPushStatus4G:
		log.Printf("    TODO: CmdClientPush4G Status")
	default:
		timer := time.After(time.Second * readWriteTimeout)
		select {
		case <-timer:
			log.Printf("Send message to channel timeout")
		case Peers[id].ResponseCh <- frame.Bytes():
		}

	}
}

func (srv *Server) checkLiveness() {
	log.Printf("Check liveness interval: %d seconds.", srv.CheckLivenessInterval)
	log.Printf("Liveness timeout: %d seconds.", srv.LivenessTimeout)
	tick := time.Tick(time.Second * time.Duration(srv.CheckLivenessInterval))
	for {
		select {
		case <-tick:
			for id, peer := range Peers {
				if time.Now().Sub(peer.PingTime) > time.Second*time.Duration(srv.LivenessTimeout) {
					//TODO call API to notify outer backend
					log.Printf("client [id: %s] timedout,kick it out!", id)
					deletePeer(id)
				}
			}
		}
	}
}

func deletePeer(id string) {
	peer, ok := Peers[id]
	if ok {
		peer.Conn.Close()
		close(peer.ResponseCh)
		delete(Peers, id)
	}
}

func (srv *Server) startRestAPI() {
	log.Printf("REST API server listen on: %v\n", srv.APIAddress)
	http.ListenAndServe(srv.APIAddress, srv.Router)
}

func (srv *Server) startLockerServer() {
	ln, err := net.Listen(srv.Protocol, srv.Address)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Locker server listen on: %v(%v)\n", srv.Address, srv.Protocol)
	for {
		conn, err := ln.Accept()
		if err != nil {
			continue
		}
		log.Printf("[New client coming...]")
		go handleConn(conn)
	}
}

func (srv *Server) Stop() {
	srv.quit <- struct{}{}
}
